home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Eagles Nest BBS 8
/
Eagles_Nest_Mac_Collection_Disc_8.TOAST
/
Developer Tools⁄Additions
/
Protocols
/
YMODEM.C
< prev
Wrap
Text File
|
1989-03-13
|
21KB
|
969 lines
/*
* Copyright 1984,1985 Omen Technology Inc All Rights Reserved
* Ward Christensen Protocol handler for sending and receiving
* ascii and binary files. Modified for choice of checksum or crc.
* This code may be used for developing YMODEM support ONLY IF
* the program documentation acknowledges the origin of this code.
*/
#ifdef CPM
EXT char defdisk; /* Default disk */
EXT char origdisk; /* Disk originally logged into */
#define PSTRLEN 33 /* For misc string params */
#ifndef PATHLEN
#define PATHLEN 65 /* Plenty long */
#endif
#endif
#define CTRL(v) ('v' & 037)
#define ERROR (-1)
#define BACKUP (-3) /* Returned to expand to access prev file */
#define NOTFOUND (-4) /* Returned to expand to indicate can't open */
#define FOUNDIT (-5) /* Function found what it was looking for */
#define OK 0
#define TRUE 1
#define FALSE 0
#define SAYTERM 2 /* Doykbd returns this if sayterm needed */
#define SECSIZ 128
#define CPMEOF 0x1A
/*
* Some important usage() error numbers
*/
#define SJ_ABORT 20 /* Keyboard abort via abort key */
EXT FILE *cfin, /* Surrogate input getcty, kgets */
*fin, /* For file uploads */
*fout, /* For file downloads */
*cout, /* For text capture and utility command redirection */
*fcall; /* script reader */
EXT char cfast; /* BDS C fastest access is to extern's */
EXT char checksum; /* Delcared here for speed */
EXT unsigned oldcrc; /* Accumulates CRC checksum */
EXT int wcj, firstch, errors;
EXT int firstsec; /* First sector, C instead of NAK for crc */
#define CMDLEN 130
#define SOH 1
#define STX 2
#define ETX 3
#define EOT 4
#define ENQ 5
#define ACK 6
#define SO 016
#define SI 017
#define DLE 020
#define XON 021
#define XOFF 023
#define NAK 025
#define CAN 030
#define ESC 033
#define WANTCRC 0103 /* Send C not NAK to get crc not checksum */
#define WANTG 0107 /* Send G not NAK to get nonstop batch xmsn */
#define TIMEOUT (-2)
#define WAICHR '\336' /* Causes a wait in put[w] and answerback */
#define RETRYMAX 10
#define RETRYNOCRC 4 /* Drop CRC request after 4 retries */
#define KSIZE 1024 /* Block length with k option */
/* Declare all globally used functions not returning int */
char *index(), *cisubstr(), *stem();
char *ascgettime(), *ascgetd(), *ascexpftime();
char **gettoken();
long fseek(), toutime(), time();
unsigned updetime();
FILE *xfopen();
This code has been modified from the Professional-YAM code with
EXT unsigned Etime; /* Elapsed time in seconds */
EXT struct tsruct calltime; /* Set when connection is made */
EXT int Zone; /* Zone in minutes from GMT */
EXT int Thiszone; /* Timezone to use for this command */
/*
* Structure passed by e1xpand to called function.
* most just use the first string part (sorry, lint!)
*/
#define UFNSIZE 68 /* 64 byte pathlen plus drive */
#define FNLENGTH 15 /* Directory filename */
#define DONOMATCH 0x8000 /* Always expand pathspecs */
struct expf {
char expfname[UFNSIZE]; /* ASCIZ file name */
long expflen; /* File length in bytes */
unsigned exptime;
unsigned expdate;
char expfn[FNLENGTH]; /* Filename only no path */
char expfattr; /* File attribute */
};
EXT char Cname[PATHLEN], Rname[PATHLEN], Tname[PATHLEN]; /* Saved filenames */
#define MAXPACK 94 /* Maximum Kermit packet size */
#define RBUFL 200 /* Kermit Receive buffer length */
/* Some declarations for USQ feature */
#define SQMAGIC 0xFF76 /* SQueezed file prefix */
#define KSQMAGIC 0xFF75 /* Cipher key magic word */
#define KEYSIZE 4096 /* Max size of key file */
#define NUMVALS 257 /* 256 data values plus SPEOF*/
extern union { /* Decoding tree for usq feature */
struct {
char ffxpkt[MAXPACK+8]; /* Far fetch MUST BE FIRST */
char rxpkt[RBUFL+2]; /* Receive packet buffer */
char txpkt[MAXPACK+8]; /* Packet buffer */
char sxpkt[MAXPACK+8]; /* Server command buffer */
char dxpkt[MAXPACK+8]; /* Packet buffer */
char axbuf[CMDLEN+2]; /* For CB command, etc. */
char kfilnam[PATHLEN+2];
} k;
char ubuf[KSIZE+2];
struct {
int children[2]; /* Left, Right */
} dnode[NUMVALS - 1];
} U;
EXT FLAG Key; /* True iff unsqueezing encrypted file */
expanations added for support routines not included.
#include "yamsys.h" /* Installation specific stuff */
#include "yam.h"
#define WCEOT (-10)
long Modtime; /* Unix style mod time for incoming file */
int Usemtime; /* <>0: Accept file mod time */
int Filemode; /* Unix style mode for incoming file */
char rxcmdchar; /* NAK, C, or G to specify rx mode */
extern char *Rcmdlog; /* Remote commands logged to this file */
static FLAG eotseen; /* <>0 if an EOT has been seen for this rx packet */
wcsend(argc, argp)
char **argp;
{
int wcs();
Crcflg = FALSE;
firstsec = TRUE;
if (Batch) {
lhmargin();
printf("Sending in Batch Mode\n");
/* Expand calls the specified function once for each file, with that
pathname as argument */
if (expand(wcs, argc, argp, 0) == ERROR)
goto fubar;
if (wctxpn("") == ERROR)
goto fubar;
}
else {
for (; --argc>=0;) {
/* Opentx opens a file for sending - `ala fopen(name, "rb" .. */
if (opentx(*argp++) == ERROR)
goto fubar;
if (wctx() == ERROR)
goto fubar;
}
}
return OK;
fubar:
closetx('E'); ++Errcnt;
canit();
return ERROR;
}
wcs(ufn)
register struct expf *ufn;
{
if (opentx(ufn->expfname) == ERROR)
return OK; /* skip over inaccessible files */
if (wctxpn(ufn) == ERROR)
return ERROR;
if (wctx() == ERROR)
return ERROR;
return OK;
}
wcreceive(argc, argp)
char **argp;
{
rxcmdchar = NAK;
if (Batch || argc == 0)
Crcflg = Batch = TRUE;
if (Crcflg)
rxcmdchar = WANTCRC;
if (Optiong > 0)
Crcflg = rxcmdchar = WANTG;
if (Batch) {
lhmargin(); Batch = TRUE;
printf("Receiving in Batch Mode\n");
if (argc)
usage(11);
for (;;) {
if (wcrxpn(U.ubuf)== ERROR)
goto fubar;
if (U.ubuf[0] == 0) {
pstat(""); return OK;
}
procheader(U.ubuf);
if (wcrx(U.ubuf) == ERROR)
goto fubar;
}
}
else {
procheader(NULL);
if (Xmodem)
printf("Receive:'%s' FILE OPEN\n", *argp);
if (wcrx(*argp) == ERROR)
goto fubar;
}
Bytesleft = DEFBYTL; return OK;
fubar:
Bytesleft = DEFBYTL; canit(); closerx(eotseen?'Q':ERROR);
++Errcnt; return ERROR;
}
/*
* Fetch a pathname from the other end as a C ctyle ASCIZ string.
* Length is indeterminate as long as less than blklen
* a null string represents no more files
*/
wcrxpn(rpn)
char *rpn; /* receive a pathname */
{
purgeline();
Crcflg = firstsec = TRUE;
lpstat("Fetching pathname");
totsecs = -1;
if (wcgetsec(rpn, rxcmdchar) != 0)
return ERROR;
/* Send the ACK unless g option */
if (Optiong <= 0)
sendline(ACK);
FLUSHMO;
return OK;
}
wctxpn(ufn)
struct expf *ufn;
{
register char *p, *q;
int i;
#ifdef MTIME
long mdt; struct dstruct d; struct tsruct t;
#endif
totsecs = -1;
pstat("Awaiting pathname NAK");
if (getnak())
return ERROR;
for (i = KSIZE, q = U.ubuf; --i >= 0; )
*q++ = 0;
p = ufn->expfname;
if (*p) {
#ifdef MTIME
tdexpftime(&d, &t, ufn);
mdt = toutime(&d, &t) + Thiszone*60L;
#endif
if (!Fullpath) {
p = stem(p);
uncaps(p);
}
for(q = U.ubuf; *p; ) /* don't send drive: */
if((*q++ = *p++) == ':')
q = U.ubuf;
/* transmit file length */
#ifdef MTIME
sprintf(++q, "%ld %lo 0 %ld", ufn->expflen, mdt, Serialn);
#else
sprintf(++q, "%ld 0 0 %ld", ufn->expflen, Serialn);
#endif
}
if (wcputsec(U.ubuf, 0, SECSIZ) == ERROR) {
wcperr("Can't send pathname %s", p);
return ERROR;
}
return OK;
}
getnak()
{
register c;
Lastrx = 0;
for (;;) {
FLUSHMO;
switch (firstch = readline(400)) {
case TIMEOUT:
return TRUE;
case WANTG:
Optiong = 1; blklen = 1024;
/* **** FALL THRU TO **** */
case WANTCRC:
Crcflg = TRUE;
/* **** FALL THRU TO **** */
case NAK:
if ((c=readl0()) == TIMEOUT)
return FALSE;
if (c == 'K') { /* IMP hack */
blklen = 1024; return FALSE;
}
/* **** FALL THRU TO **** */
case CAN:
if (Lastrx == CAN)
return TRUE;
/* **** FALL THRU TO **** */
default:
showctl(firstch);
}
Lastrx = firstch;
}
}
/*
* Adapted from CMODEM13.C, written by
* Jack M. Wierda and Roderick W. Hart
*/
wcrx(name)
char *name;
{
register char *p;
register cblklen; /* bytes to dump this block */
int sendchar, sectnum, sectcurr;
int syntry; /* number of tries to get right sec */
if (openrx(name) == ERROR)
return ERROR;
firstsec = TRUE; eotseen = FALSE; totsecs = sectnum = 0;
sendchar = rxcmdchar;
for (;;) {
syntry = 0; /* # of tries for right sector */
synagain:
sectcurr = wcgetsec(U.ubuf, sendchar);
if (sectcurr == (sectnum+1 & 0377)) {
sectnum++;
/*
* if the compiler supports longs && the o/s records the
* exact length of files then and only then use the file length
* info (if transmitted).
*/
wcj = cblklen = Bytesleft>blklen ? blklen:Bytesleft;
Charsrx += wcj;
if (Overlapio) {
if (Optiong <= 0)
sendline(ACK);
sendchar = -1;
} else
sendchar=ACK;
FLUSHMO;
for (p = U.ubuf; --wcj>=0; )
if (putc(*p++, fout) == ERROR)
usage(30);
if (View) {
wcj = cblklen;
for (p = U.ubuf;--wcj>=0;)
bttyout(*p++);
}
if ((Bytesleft -= cblklen) < 0)
Bytesleft = 0;
totsecs += blklen/128;
}
else if (sectcurr == (sectnum & 0377)) {
wcperr("Received dup Sector");
sendchar = ACK;
}
else if (sectcurr == WCEOT) {
sendline(ACK);
FLUSHMO;
/* Don't pad the file any more than it already is */
closerx('R');
#ifdef MTIME
if (Modtime) {
sutime(Rname, Modtime - Thiszone*60L);
Modtime = 0L;
}
#endif
return OK;
}
else if (sectcurr == ERROR)
return ERROR;
else {
wcperr("Sync Error: got %d", sectcurr);
if (++syntry < 12) {
sendchar = Batch ? WANTCRC : NAK;
goto synagain;
}
return ERROR;
}
}
}
/*
* wcgetsec fetches a Ward Christensen type sector.
* Returns:
* sector number encountered
* or ERROR if valid sector not received, or CAN CAN received
* or WCEOT if eot sector
***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
* (Caller must do that when he is good and ready to get next sector)
*/
wcgetsec(rxbuf, sendchar)
char *rxbuf;
{
register char *p;
register c;
register sectcurr;
register maxtim = 100;
eotseen = FALSE;
for (Lastrx = errors = 0; errors < RETRYMAX; ++errors, ++toterrs) {
if(Ctrlbrk)
return ERROR;
if (firstsec && !Batch && errors >= RETRYNOCRC ) {
sendchar = NAK; Crcflg = FALSE;
}
showsec();
if (sendchar > 0) {
sendline(sendchar); /* Send it now, we're ready! */
}
FLUSHMO;
blklen = SECSIZ;
noisereject:
switch (c = readline(maxtim)) {
case CAN:
if (Lastrx == CAN) {
wcperr("Sender CANcelled");
return ERROR;
} else {
Lastrx = CAN;
continue;
}
case EOT:
if (Optiong)
return WCEOT;
++eotseen;
/* make sure eot really is eot and not just mixmash */
if (readl0() == TIMEOUT) {
if (!Crcflg || Lastrx == EOT)
return WCEOT;
Lastrx = EOT; goto bilge3;
}
goto bilge;
case STX:
blklen = KSIZE;
case SOH:
sectcurr = readline(50);
if ((sectcurr+readline(50)) == 0377) {
checksum = oldcrc = 0;
for (p = rxbuf,wcj = blklen; --wcj>=0; ) {
if ((c = readline(50)) < 0)
goto bilge;
oldcrc = updcrc(c, oldcrc);
checksum += (*p++ = c);
}
if ((c = readline(50)) < 0)
goto bilge;
if (Crcflg) {
oldcrc = updcrc(c, oldcrc);
if ((c = readline(50)) < 0)
goto bilge;
oldcrc = updcrc(c, oldcrc);
if (oldcrc) {
wcperr("Bad CRC=%04x",
oldcrc);
goto bilge2;
}
else {
firstsec = FALSE;
return sectcurr;
}
}
else if ((checksum-c) & 0377) {
wcperr("Checksum Bad rx=%02x cx=%02x",
c, checksum);
goto bilge2;
} else {
firstsec = FALSE;
return sectcurr;
}
}
wcperr("Sector number garbled"); goto bilge;
default:
if (Crcflg)
goto noisereject;
case TIMEOUT:
break;
}
bilge:
switch (c) {
case TIMEOUT:
wcperr("Timeout"); goto bilge3;
case ERROR: case (ERROR & ~0200):
Mcstat = 0;
wcperr("Modem SR=%02x", Mcstat);
break;
default:
wcperr("Got %02x sector header", c);
break;
}
bilge2:
junkpacket();
bilge3:
if (firstsec)
sendchar = rxcmdchar;
else {
maxtim = 50;
sendchar = NAK;
}
}
/* try to stop the bubble machine. */
canit(); return ERROR;
}
/*VARARGS1*/
wcperr(s,p,q)
char *s, *p, *q;
{
char bufx[70];
llhmargin();
sprintf(bufx, s, p, q);
lprintf("Sector %3d error %d: %s\n", totsecs, errors, bufx);
#ifdef DEBUG2
if ( !Serialn) {
logfile(Rcmdlog, bufx, firstch, (long)totsecs);
if (Xmodem && !carrier())
logfile(Rcmdlog, "CARRIER LOST", 'C', (long)updetime());
}
#endif
}
showsec()
{
if (!Quiet)
pstat("Sector %3d %2dk %s",
totsecs, totsecs/8, Crcflg?"CRC-16":"" );
}
wctx()
{
register char *p;
register unsigned sectnum;
firstsec = TRUE; totsecs = 0;
pstat("Awaiting initial NAK");
if (getnak())
return ERROR;
sectnum = 1;
while (filbuf(U.ubuf, blklen)) {
totsecs += (blklen/128);
showsec();
if (wcputsec(U.ubuf, sectnum, blklen) == ERROR) {
#ifdef DEBUG2
if ( !Serialn)
logfile(Rcmdlog, "SE1", firstch, Charstx);
#endif
if (firstch == WANTCRC && sectnum > 1) {
long lseek();
if (lseek(fileno(fin), -blklen*2L, 1) == -1L)
return ERROR;
--sectnum; Charstx -= blklen;
totsecs -= (blklen/64);
wcperr("Resynchronizing");
continue;
}
return ERROR;
} else {
if (View)
for (p = U.ubuf,wcj = blklen;--wcj>=0;)
bttyout(*p++);
++sectnum; Charstx += blklen;
}
}
closetx('S');
for (errors = 0; ++errors<RETRYMAX; ) {
purgeline();
sendline(EOT);
FLUSHMO;
if (Ctrlbrk)
break;
if((firstch=readline(100)) == ACK || firstch == (ACK|0200))
return OK;
wcperr("Got %02x for ACK to EOT", firstch);
}
wcperr("No ACK on EOT");
return ERROR;
}
wcputsec(txbuf, sectnum, cseclen)
char *txbuf;
register unsigned sectnum;
int cseclen; /* data length of this sector to send */
{
register char *p;
FLAG nogood;
firstch = 0; /* part of logic to detect CAN CAN */
for (errors = 0; errors < RETRYMAX; ++errors, ++toterrs) {
nogood = FALSE;
if (Ctrlbrk)
goto cancan;
Lastrx = firstch;
sendline(cseclen == KSIZE?STX:SOH);
sendline(sectnum);
sendline(-sectnum-1);
oldcrc = checksum = 0;
for (wcj = cseclen,p = txbuf; --wcj>=0; ) {
sendline(*p);
oldcrc = updcrc(*p, oldcrc);
checksum += *p++;
if (miinqueue()) {
switch (michar()) {
case CAN:
goto chkcan;
case XOFF:
readline(500); break;
default:
break;
}
}
}
if (Crcflg) {
oldcrc = updcrc(0,updcrc(0,oldcrc));
sendline(oldcrc>>8);sendline(oldcrc);
}
else
sendline(checksum);
if (Ctrlbrk)
goto cancan;
if (miready()) {
wcperr("Noise Burst Detected");
if (Optiong > 0)
goto cancan;
nogood = TRUE; purgeline();
}
if (Optiong) {
firstsec = FALSE; return OK;
}
FLUSHMO;
firstch = readline(400);
gotnak:
switch (firstch) {
case CAN:
chkcan:
if(Lastrx == CAN) {
cancan:
wcperr("Receiver Cancelled"); return ERROR;
}
break;
case TIMEOUT:
wcperr("Timeout on sector ACK"); continue;
case WANTCRC:
if (firstsec)
Crcflg = TRUE;
case NAK:
wcperr("NAK on sector");
#ifdef NOTDEF
/* ******* TESTING ********** */
{
extern Quitafter;
if (Quitafter && Verbose) {
wcperr("Faking ACK: ALT-Q");
Quitafter = FALSE; return OK;
}
}
#endif
continue;
case ACK:
gotack:
if (nogood)
break;
if (!Tfile || (sectnum==0) || (readl0()==TIMEOUT)) {
firstsec = FALSE; return OK;
}
wcperr("Got burst for sector ACK");
break;
case ACK|0200:
if ( !Tfile)
goto gotack;
default:
wcperr("Got %02x for sector ACK", firstch); break;
}
for (;;) {
Mcstat = 0;
Lastrx = firstch;
if ((firstch = readline(400)) == TIMEOUT)
break;
if ((firstch == NAK || firstch == WANTCRC)
&& readl0() == TIMEOUT)
goto gotnak;
if (firstch == CAN && Lastrx == CAN)
goto cancan;
/* Let user see it if strange char */
showctl(firstch);
}
}
wcperr("No ACK on sector");
return ERROR;
}
/* Throw away incoming packet */
junkpacket()
{
register n = 3600;
register c;
register b;
b = Ctrlbrk; Ctrlbrk = 0;
pstat("%sError Recovery", Outarev);
while (--n) {
if (cdo()) {
prcdo(); goto done;
}
switch (c = readline(20)) {
case TIMEOUT:
goto done;
case CAN:
case ETX:
if (c == readline(10)) {
b=c; goto done;
}
}
}
done: Ctrlbrk |= b; Mcstat = 0;
pstat("");
}
/*
* Send 5 CAN's to try to get the other end to shut up
* then 5 backspaces to edit out the CAN's
*/
canit()
{
register c;
junkpacket();
for (c = 5; --c>=0; )
sendline(CAN);
for (c = 5; --c>=0; )
sendline('\b');
FLUSHMO;
}
/* Fill buf with count chars padding with ^Z for CPM */
filbuf(buf, count)
char *buf;
{
register m;
#ifdef NOTDEF
register c;
m = count;
while((c=getc(fin))!=EOF) {
*buf++ =c;
if(--m == 0)
break;
}
if(m == count)
return 0;
else
while(--m >= 0)
*buf++ = 032;
return count;
#else
m = read(fileno(fin), buf, count);
if (m <= 0)
return 0;
while (m < count)
buf[m++] = 032;
return count;
#endif
}
/*
* Process incoming header
*/
procheader(name)
register char *name;
{
register char *p;
long theirsn;
extern char Instmsg[];
/* set default parameters */
Bytesleft = DEFBYTL; Filemode = 0666; Modtime = 0L;
if (name) {
p = name + 1 + strlen(name);
if (*p) { /* file coming from Unix type system */
sscanf(p, "%ld%lo%o%lo", &Bytesleft,
&Modtime, &Filemode, &theirsn);
#ifndef DEMO
if (Serialn && theirsn == Serialn) {
Instmsg[0] = 0; usage(26);
}
#endif
}
}
}
/*
* Routines to convert to/from Unix(TM) 32 bit time code (epoch:1970).
* Good for times 1970 to 2035 approximately, by which time we should
* be timestamping files in microseconds since the Big Bang ...
*
* Original factor routine from Lynn Long
*/
#define EPOCH 719542L
long int factor(mm, dd, yy)
long int mm, dd, yy;
{
long int raw;
raw = 365 * yy + dd -1 + 31 * (mm - 1);
if(mm < 3)
raw += ((yy -1)/4);
else
raw -= (23 + 4*mm)/10L - yy/4;
return(raw);
}
#ifdef MTIME
struct dstruct { /* Structure filled in by msdate() */
int year;
char month;
char day;
};
struct tsruct { /* Structure filled in by mstod() */
char hrs;
char mins;
char secs;
char csecs;
};
static dayinmo[]= { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
#define DAYSEC 86400L
#define DAYPYR(y) (((y&03)) ? 365:366)
/*
* Unpacks Unix format time into dstruct and tsruct structures
*/
unutime(ds, ts, t)
register struct dstruct *ds;
register struct tsruct *ts;
long t;
{
register int c;
register long days, rem, y, mo;
rem = t % DAYSEC; days = t / DAYSEC;
ts->csecs = 0;
ts->secs = rem % 60L; rem /= 60L;
ts->mins = rem % 60L; ts->hrs = rem/60L;
for (y=1970; days>=(c=DAYPYR(y)); ++y)
days -= c;
dayinmo[1] = c==366 ? 29:28;
for (mo=0; days>=(c=dayinmo[mo]); ++mo)
days -= c;
ds->day = days+1; ds->month = mo+1; ds->year = y - 1980;
#ifdef DEBUG
lprintf("Unutime: da %d mo %d yr %d %d:%d:%d\n",
ds->day, ds->month, ds->year, ts->hrs, ts->mins, ts->secs);
#endif
}
/*
* Packs dstruct and tsruct structures into Unix format time
*/
long
toutime(ds, ts)
register struct dstruct *ds;
register struct tsruct *ts;
{
register long t;
t = factor((long)ds->month, (long)ds->day, ds->year + 1980L);
t -= EPOCH; t *= DAYSEC;
t += ts->secs; t += ts->mins*60; t += ts->hrs*3600L;
#ifdef DEBUG
lprintf("Toutime: da %d mo %d yr %d %d:%d:%d %lo\n",
ds->day, ds->month, ds->year, ts->hrs, ts->mins, ts->secs, t);
#endif
return t;
}
/*
* Sets file s to Unix time t
*/
sutime(s, ti)
char *s;
long ti;
{
struct dstruct ds;
struct tsruct ts;
register unsigned d, t;
unutime(&ds, &ts, ti);
d = ds.day + (ds.month << 5) + (ds.year << 9);
t = (ts.secs >> 1) + (ts.mins << 5) + (ts.hrs << 11);
#ifdef DEBUG
lprintf("Sutime: d=%x t=%x", d, t);
lprintf(" da %d mo %d yr %d %d:%d:%d\n",
ds.day, ds.month, ds.year, ts.hrs, ts.mins, ts.secs);
#endif
if (Usemtime)
utime(s, d, t);
}
/*
* Unpack time from struct expf to tsruct and dstruct
*/
tdexpftime(ds, ts, e)
register struct expf *e;
register struct dstruct *ds;
register struct tsruct *ts;
{
ts->hrs = (e->exptime>>11);
ts->mins = ((e->exptime>>5)&077);
ts->secs = ((e->exptime&037)<<1);
ds->month = ((e->expdate>>5)&017);
ds->day = (e->expdate&037);
ds->year = ((e->expdate>>9)%100);
#ifdef DEBUG
lprintf("TDEX.. da %d mo %d yr %d %d:%d:%d\n",
ds->day, ds->month, ds->year, ts->hrs, ts->mins, ts->secs);
#endif
}
#endif /* M T I M E */